#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>

/*  头文件
 *  #include <sys/types.h>		//定义了一些常用数据类型，比如size_t
 *  #include <fcntl.h>			//定义了open、creat等函数，以及表示文件权限的宏定义
 *  #include <unistd.h>			//定义了read、write、close、lseek等函数
 *  #include <errno.h>			//与全局变量errno相关的定义
 *  #include <sys/ioctl.h>		//定义了ioctl函数 
 *
 *  read — 读取文件
 *  函数原型如下：ssize_t read(int fd, void *buf, size_t count);
 *  函数参数含义如下：
 *  fd：文件描述符
 *  buf：用来接收所读数据的缓冲区
 *  count：请求读取的字节数
 *  返回值：读取成功则返回读取的字节数，读取到文件尾则返回0，读取失败则返回-1，同时设置全局变量errno的值来表示错误原因；
 * 
 *  write — 写入文件
 *  函数原型如下：ssize_t write(int fd, const void *buf, size_t count);
 *  函数参数含义如下：
 *  fd：文件描述符
 *  buf：存放待写入数据的缓冲区
 *  count：请求写入的字节数
 *  返回值：写入成功则返回实际写入的字节数，写入失败则返回-1，同时设置全局变量errno的值来表示错误原因；
 *  
 *  close — 关闭文件
 *  API原型如下：
 *  int close(int fd);
 *  函数参数fd表示要关闭文件的文件描述符。
 *  如果关闭成功，返回0，否则返回-1，同时设置全局变量 errno 报告具体错误的原因。
 * 
 *  open — 打开文件
 *  操作文件之前必须要打开文件，获取文件描述符fd，该函数原型如下：
 *  int open(const char *pathname, int flags);
 *  int open(const char *pathname, int flags,mode_t mode);
 *  参数含义如下：
 *  pathname：文件路径+文件名称
 *  flags：文件打开方式
 *  mode：打开文件的权限
 *  返回值int：打开成功则返回文件描述符，打开失败则返回-1，同时设置全局变量errno的值来表示错误原因；
 *  flags标志的值可以使用在<fcntl.h>的宏定义：
 *  O_RDONLY：只读
 *  O_WRONLY：只写
 *  O_RDWR：可读可写（常用）
 *  O_CREAT：如果要打开的文件不存在，则创建新文件
 *  O_EXCL：如果使用O_CREAT时文件已经存在，则返回错误消息
 *  O_TRUNC：如果文件已经存在，且成功打开，则删除文件中原来的全部数据
 *  O_APPEND：以追加写入方式打开文件，打开之后文件指针指向文件末尾
 * 
 */

// 1 read write 方式 0 mmap方式
#define pattern 0

#if pattern

int main(int argc, char **argv){
    int fd_old , fd_new ;
    char buf[1024];
    int len;

    /*判断参数*/
    if (argc != 3){
        printf("Usage: %s <old-file> <new-file>\n", argv[0]);
        return -1;    
    }
    /*打开老文件*/
    fd_old = open(argv[1] , O_RDONLY); //只读方式打开
    if (fd_new == -1)
    {
        printf("can not open file %s\n", argv[1]);
        return -1;
    }
    /*创建新文件*/
    fd_new = open(argv[2] , O_WRONLY | O_CREAT |O_TRUNC); //创建文件
    if(fd_new == -1){
        printf("can not create file %s\n", argv[2]);
        return -1;
    }
    /*循环 读老文件 写新文件*/
    while (len = read(fd_old, buf,1024)){
        printf("len: %d\n",len);
        if( write(fd_new, buf, len ) != len ){
            printf("can not write file %s\n", argv[2]);
            return -1;
        }
    }
    printf("copying file succeed!!!!\n");
    // while(len = read(fd_old, buf,1024))
    // {
    //     printf("len: %d\n",len);
    // }

    // printf("read off!!!!\n");

    /*关闭文件*/
    close(fd_old);
    close(fd_new);
}

#else

/*
 *  下面我们介绍一下怎么使用 mmap，mmap 函数的原型如下：
 *  addr：指定映射的虚拟内存地址，可以设置为 NULL，让 Linux 内核自动选择合适的虚拟内存地址。
 *  length：映射的长度。
 *  prot：映射内存的保护模式，可选值如下：
 *  PROT_EXEC：可以被执行。
 *  PROT_READ：可以被读取。
 *  PROT_WRITE：可以被写入。
 *  PROT_NONE：不可访问。
 *  flags：指定映射的类型，常用的可选值如下：
 *  MAP_FIXED：使用指定的起始虚拟内存地址进行映射。
 *  MAP_SHARED：与其它所有映射到这个文件的进程共享映射空间（可实现共享内存）。
 *  MAP_PRIVATE：建立一个写时复制（Copy on Write）的私有映射空间。
 *  MAP_LOCKED：锁定映射区的页面，从而防止页面被交换出内存。
 *  ...
 *  fd：进行映射的文件句柄。
 *  offset：文件偏移量（从文件的何处开始映射）。
 */


int main(int argc, char **argv){
    int fd_old , fd_new ;
    struct stat stat;
    char *buf;

    /*判断参数*/
    if (argc != 3){
        printf("Usage: %s <old-file> <new-file>\n", argv[0]);
        return -1;    
    }
    /*打开老文件*/
    fd_old = open(argv[1] , O_RDONLY); //只读方式打开
    if (fd_new == -1){
        printf("can not open file %s\n", argv[1]);
        return -1;
    }
    /*确定老文件大小*/
    if(fstat(fd_old,&stat) == -1){
        printf("can not get stat of file %s\n", argv[1]);
        return -1;
    }
    /*映射老文件*/
    buf = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd_old,0);
    if(buf == MAP_FAILED){
        printf("can not map file %s\n", argv[1]);
        return -1;
    }
    /*创建新文件*/
    fd_new = open(argv[2], O_WRONLY | O_CREAT |O_TRUNC , S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); //创建文件
    if(fd_new == -1){
        printf("can not create file %s\n", argv[2]);
        return -1;
    }
    /*写文件*/
    if(write(fd_new,buf,stat.st_size) != stat.st_size){
        printf("can not write file %s\n", argv[2]);
        return -1;
    }
    /*关闭文件*/
    munmap(buf, stat.st_size);
    close(fd_old);
    close(fd_new);
}


#endif